<template>
    <div id="app" :class="{ 'dark-theme': isDarkMode, 'light-theme': !isDarkMode }">
        <!-- 静态背景 -->
        <div class="background"></div>

        <!-- 主题切换按钮 -->
        <button class="theme-toggle glass-morphism" @click="toggleTheme">
            <span v-if="isDarkMode">☀️</span>
            <span v-else>🌙</span>
        </button>

        <header class="header">
            <div class="header-content glass-morphism">
                <h1>情感数据可视化分析</h1>
                <p>基于老君山旅游评论的情感分析可视化展示</p>
            </div>
        </header>

        <div class="container">
            <div class="stats-grid" v-if="stats">
                <div class="stat-card glass-morphism">
                    <div class="stat-icon">📊</div>
                    <h3>总评论数</h3>
                    <div class="stat-value">{{ stats.total.toLocaleString() }}</div>
                </div>
                <div class="stat-card glass-morphism positive">
                    <div class="stat-icon">😊</div>
                    <h3>正面评论</h3>
                    <div class="stat-value">{{ stats.positiveCount.toLocaleString() }}</div>
                    <div class="stat-percentage">{{ ((stats.positiveCount / stats.total) * 100).toFixed(1) }}%</div>
                </div>
                <div class="stat-card glass-morphism negative">
                    <div class="stat-icon">😞</div>
                    <h3>负面评论</h3>
                    <div class="stat-value">{{ stats.negativeCount.toLocaleString() }}</div>
                    <div class="stat-percentage">{{ ((stats.negativeCount / stats.total) * 100).toFixed(1) }}%</div>
                </div>
                <div class="stat-card glass-morphism neutral">
                    <div class="stat-icon">😐</div>
                    <h3>中性评论</h3>
                    <div class="stat-value">{{ stats.neutralCount.toLocaleString() }}</div>
                    <div class="stat-percentage">{{ ((stats.neutralCount / stats.total) * 100).toFixed(1) }}%</div>
                </div>
            </div>

            <div class="charts-grid">
                <div class="chart-container glass-morphism">
                    <div class="chart-header">
                        <h2>情感分布饼图</h2>
                        <div class="chart-subtitle">基于 SnowNLP 情感得分分类</div>
                    </div>
                    <div class="chart-wrapper">
                        <div id="sentiment-pie-chart"></div>
                    </div>
                </div>

                <div class="chart-container glass-morphism">
                    <div class="chart-header">
                        <h2>评分分布柱状图</h2>
                        <div class="chart-subtitle">用户评分统计分布</div>
                    </div>
                    <div class="chart-wrapper">
                        <div id="rating-bar-chart"></div>
                    </div>
                </div>

                <div class="chart-container glass-morphism full-width">
                    <div class="chart-header">
                        <h2>时间线情感趋势</h2>
                        <div class="chart-subtitle">正面评论随时间变化趋势</div>
                    </div>
                    <div class="chart-wrapper">
                        <div id="timeline-chart"></div>
                    </div>
                </div>

                <div class="chart-container glass-morphism">
                    <div class="chart-header">
                        <h2>地区情感分析</h2>
                        <div class="chart-subtitle">各地区评论数量分布</div>
                    </div>
                    <div class="chart-wrapper">
                        <div id="region-chart"></div>
                    </div>
                </div>

                <div class="chart-container glass-morphism">
                    <div class="chart-header">
                        <h2>情感得分散点图</h2>
                        <div class="chart-subtitle">情感得分与评分关系</div>
                    </div>
                    <div class="chart-wrapper">
                        <div id="sentiment-scatter"></div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 加载遮罩 -->
        <div v-if="!stats" class="loading-overlay">
            <div class="loading-spinner"></div>
            <p>正在加载数据...</p>
        </div>

        <!-- 错误信息 -->
        <div v-if="error" class="error-overlay">
            <div class="error-message">
                <h3>数据加载失败</h3>
                <p>{{ error }}</p>
                <button @click="fetchData" class="retry-button">重试</button>
            </div>
        </div>
    </div>
</template>

<script>
import { ref, onMounted } from 'vue'
import * as d3 from 'd3'
import axios from 'axios'

export default {
    name: 'App',
    setup() {
        const stats = ref(null)
        const rawData = ref([])
        const timeline = ref([])
        const isDarkMode = ref(true) // 默认暗色模式
        const error = ref(null)

        // 创建 axios 实例，基础配置
        const api = axios.create({
            baseURL: 'http://localhost:3001',
            timeout: 10000,
            headers: {
                'Content-Type': 'application/json'
            }
        })

        // 请求拦截器，用于加载状态
        api.interceptors.request.use(
            config => {
                error.value = null
                return config
            },
            error => {
                return Promise.reject(error)
            }
        )

        // 响应拦截器，用于错误处理
        api.interceptors.response.use(
            response => response,
            error => {
                console.error('API Error:', error)
                if (error.code === 'ECONNABORTED') {
                    error.value = '请求超时，请检查网络连接'
                } else if (error.response) {
                    error.value = `服务器错误: ${error.response.status}`
                } else if (error.request) {
                    error.value = '无法连接到服务器，请检查服务器是否启动'
                } else {
                    error.value = '请求配置错误'
                }
                return Promise.reject(error)
            }
        )

        const toggleTheme = () => {
            isDarkMode.value = !isDarkMode.value
            localStorage.setItem('theme', isDarkMode.value ? 'dark' : 'light')

            // 切换主题后重绘所有图表
            setTimeout(() => {
                createSentimentPieChart()
                createRatingBarChart()
                createTimelineChart()
                createRegionChart()
                createSentimentScatter()
            }, 100)
        }

        const getThemeColors = () => {
            if (isDarkMode.value) {
                return {
                    positive: '#10B981',
                    negative: '#EF4444',
                    neutral: '#F59E0B',
                    primary: '#3B82F6',
                    secondary: '#8B5CF6',
                    text: '#6B7280'
                }
            } else {
                return {
                    positive: '#059669',
                    negative: '#DC2626',
                    neutral: '#D97706',
                    primary: '#0891b2',
                    secondary: '#0d9488',
                    text: '#1f2937'
                }
            }
        }

        const fetchData = async () => {
            try {
                error.value = null

                // 并发获取所有数据
                const [statsResponse, dataResponse, timelineResponse] = await Promise.all([
                    api.get('/api/stats'),
                    api.get('/api/data'),
                    api.get('/api/timeline')
                ])

                // 从响应中提取数据
                stats.value = statsResponse.data.stats
                rawData.value = dataResponse.data.data
                timeline.value = timelineResponse.data.timeline

                console.log('Data loaded successfully:', {
                    statsCount: stats.value?.total || 0,
                    rawDataCount: rawData.value?.length || 0,
                    timelineCount: timeline.value?.length || 0
                })

            } catch (err) {
                console.error('Error fetching data:', err)
                // 错误已由拦截器处理
            }
        }

        const createSentimentPieChart = () => {
            if (!stats.value) return

            const colors = getThemeColors()
            const data = [
                { label: '正面', value: stats.value.positiveCount, color: colors.positive },
                { label: '负面', value: stats.value.negativeCount, color: colors.negative },
                { label: '中性', value: stats.value.neutralCount, color: colors.neutral }
            ].filter(d => d.value > 0)

            const container = d3.select('#sentiment-pie-chart')
            const containerRect = container.node().getBoundingClientRect()
            const width = Math.min(containerRect.width || 350, 350)
            const height = Math.min(containerRect.height || 350, 350)
            const radius = Math.min(width, height) / 2 - 20

            container.selectAll('*').remove()

            const svg = container
                .append('svg')
                .attr('width', width)
                .attr('height', height)
                .style('display', 'block')
                .style('margin', '0 auto')

            const g = svg.append('g')
                .attr('transform', `translate(${width / 2}, ${height / 2})`)

            const pie = d3.pie()
                .value(d => d.value)
                .sort(null)

            const arc = d3.arc()
                .innerRadius(radius * 0.4)
                .outerRadius(radius)

            const arcs = g.selectAll('.arc')
                .data(pie(data))
                .enter().append('g')
                .attr('class', 'arc')

            // 添加路径并动画
            arcs.append('path')
                .attr('d', arc)
                .attr('fill', d => d.data.color)
                .attr('stroke', isDarkMode.value ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.1)')
                .attr('stroke-width', 2)
                .style('filter', 'drop-shadow(0 4px 8px rgba(0,0,0,0.1))')
                .transition()
                .duration(1000)
                .attrTween('d', function (d) {
                    const interpolate = d3.interpolate({ startAngle: 0, endAngle: 0 }, d)
                    return function (t) {
                        return arc(interpolate(t))
                    }
                })

            // 添加标签
            arcs.append('text')
                .attr('transform', d => `translate(${arc.centroid(d)})`)
                .attr('text-anchor', 'middle')
                .attr('fill', 'white')
                .attr('font-weight', 'bold')
                .attr('font-size', '14px')
                .style('text-shadow', '1px 1px 2px rgba(0,0,0,0.5)')
                .text(d => `${d.data.label}\n${d.data.value}`)
                .style('opacity', 0)
                .transition()
                .delay(1000)
                .duration(500)
                .style('opacity', 1)
        }

        const createRatingBarChart = () => {
            if (!stats.value) return

            const colors = getThemeColors()
            const data = Object.entries(stats.value.ratingDistribution)
                .map(([rating, count]) => ({ rating: +rating, count }))
                .sort((a, b) => a.rating - b.rating)

            const container = d3.select('#rating-bar-chart')
            const containerRect = container.node().getBoundingClientRect()
            const margin = { top: 20, right: 30, bottom: 40, left: 60 }
            const width = (containerRect.width || 400) - margin.left - margin.right
            const height = (containerRect.height || 300) - margin.top - margin.bottom

            container.selectAll('*').remove()

            const svg = container
                .append('svg')
                .attr('width', width + margin.left + margin.right)
                .attr('height', height + margin.top + margin.bottom)

            const g = svg.append('g')
                .attr('transform', `translate(${margin.left}, ${margin.top})`)

            const x = d3.scaleBand()
                .domain(data.map(d => d.rating))
                .range([0, width])
                .padding(0.2)

            const y = d3.scaleLinear()
                .domain([0, d3.max(data, d => d.count)])
                .range([height, 0])

            // 添加渐变
            const gradient = svg.append('defs')
                .append('linearGradient')
                .attr('id', 'barGradient')
                .attr('gradientUnits', 'userSpaceOnUse')
                .attr('x1', 0).attr('y1', height)
                .attr('x2', 0).attr('y2', 0)

            gradient.append('stop')
                .attr('offset', '0%')
                .attr('stop-color', colors.primary)

            gradient.append('stop')
                .attr('offset', '100%')
                .attr('stop-color', isDarkMode.value ? '#60A5FA' : '#67e8f9')

            // 添加柱状条并动画
            g.selectAll('.bar')
                .data(data)
                .enter().append('rect')
                .attr('class', 'bar')
                .attr('x', d => x(d.rating))
                .attr('width', x.bandwidth())
                .attr('y', height)
                .attr('height', 0)
                .attr('fill', 'url(#barGradient)')
                .attr('rx', 4)
                .style('filter', 'drop-shadow(0 2px 4px rgba(0,0,0,0.1))')
                .transition()
                .duration(1000)
                .delay((d, i) => i * 100)
                .attr('y', d => y(d.count))
                .attr('height', d => height - y(d.count))

            // 柱状条顶部数值标签
            g.selectAll('.bar-label')
                .data(data)
                .enter().append('text')
                .attr('class', 'bar-label')
                .attr('x', d => x(d.rating) + x.bandwidth() / 2)
                .attr('y', d => y(d.count) - 5)
                .attr('text-anchor', 'middle')
                .attr('fill', colors.text)
                .attr('font-size', '12px')
                .attr('font-weight', 'bold')
                .text(d => d.count)
                .style('opacity', 0)
                .transition()
                .delay(1200)
                .duration(500)
                .style('opacity', 1)

            // 添加坐标轴
            g.append('g')
                .attr('transform', `translate(0, ${height})`)
                .call(d3.axisBottom(x))
                .selectAll('text')
                .attr('fill', colors.text)

            g.append('g')
                .call(d3.axisLeft(y))
                .selectAll('text')
                .attr('fill', colors.text)
        }

        const createTimelineChart = () => {
            if (!timeline.value.length) return

            const colors = getThemeColors()
            const container = d3.select('#timeline-chart')
            const containerRect = container.node().getBoundingClientRect()
            const margin = { top: 20, right: 30, bottom: 50, left: 60 }
            const width = (containerRect.width || 800) - margin.left - margin.right
            const height = (containerRect.height || 300) - margin.top - margin.bottom

            container.selectAll('*').remove()

            const svg = container
                .append('svg')
                .attr('width', width + margin.left + margin.right)
                .attr('height', height + margin.top + margin.bottom)

            const g = svg.append('g')
                .attr('transform', `translate(${margin.left}, ${margin.top})`)

            const parseDate = d3.timeParse('%Y-%m-%d')
            const data = timeline.value.map(d => ({
                ...d,
                date: parseDate(d.date)
            })).filter(d => d.date).sort((a, b) => a.date - b.date)

            const x = d3.scaleTime()
                .domain(d3.extent(data, d => d.date))
                .range([0, width])

            const y = d3.scaleLinear()
                .domain([0, d3.max(data, d => Math.max(d.positive, d.negative, d.neutral))])
                .range([height, 0])

            // 区域渐变
            const gradient = svg.append('defs')
                .append('linearGradient')
                .attr('id', 'areaGradient')
                .attr('gradientUnits', 'userSpaceOnUse')
                .attr('x1', 0).attr('y1', height)
                .attr('x2', 0).attr('y2', 0)

            gradient.append('stop')
                .attr('offset', '0%')
                .attr('stop-color', colors.positive)
                .attr('stop-opacity', 0.1)

            gradient.append('stop')
                .attr('offset', '100%')
                .attr('stop-color', colors.positive)
                .attr('stop-opacity', 0.8)

            const line = d3.line()
                .x(d => x(d.date))
                .y(d => y(d.positive))
                .curve(d3.curveCardinal)

            const area = d3.area()
                .x(d => x(d.date))
                .y0(height)
                .y1(d => y(d.positive))
                .curve(d3.curveCardinal)

            // 添加面积
            g.append('path')
                .datum(data)
                .attr('fill', 'url(#areaGradient)')
                .attr('d', area)

            // 添加折线
            const path = g.append('path')
                .datum(data)
                .attr('fill', 'none')
                .attr('stroke', colors.positive)
                .attr('stroke-width', 3)
                .attr('d', line)
                .style('filter', 'drop-shadow(0 2px 4px rgba(0,0,0,0.1))')

            // 折线绘制动画
            const totalLength = path.node().getTotalLength()
            path
                .attr('stroke-dasharray', totalLength + ' ' + totalLength)
                .attr('stroke-dashoffset', totalLength)
                .transition()
                .duration(2000)
                .attr('stroke-dashoffset', 0)

            // 添加坐标轴
            g.append('g')
                .attr('transform', `translate(0, ${height})`)
                .call(d3.axisBottom(x).tickFormat(d3.timeFormat('%Y-%m')))
                .selectAll('text')
                .attr('fill', colors.text)
                .style('text-anchor', 'end')
                .attr('dx', '-.8em')
                .attr('dy', '.15em')
                .attr('transform', 'rotate(-45)')

            g.append('g')
                .call(d3.axisLeft(y))
                .selectAll('text')
                .attr('fill', colors.text)
        }

        const createRegionChart = () => {
            if (!stats.value) return

            const colors = getThemeColors()
            const data = Object.entries(stats.value.regionStats)
                .map(([region, stats]) => ({ region, ...stats }))
                .sort((a, b) => b.count - a.count)
                .slice(0, 10)

            const container = d3.select('#region-chart')
            const containerRect = container.node().getBoundingClientRect()
            const margin = { top: 20, right: 30, bottom: 80, left: 60 }
            const width = (containerRect.width || 400) - margin.left - margin.right
            const height = (containerRect.height || 300) - margin.top - margin.bottom

            container.selectAll('*').remove()

            const svg = container
                .append('svg')
                .attr('width', width + margin.left + margin.right)
                .attr('height', height + margin.top + margin.bottom)

            const g = svg.append('g')
                .attr('transform', `translate(${margin.left}, ${margin.top})`)

            const x = d3.scaleBand()
                .domain(data.map(d => d.region))
                .range([0, width])
                .padding(0.2)

            const y = d3.scaleLinear()
                .domain([0, d3.max(data, d => d.count)])
                .range([height, 0])

            // 添加渐变
            const gradient = svg.append('defs')
                .append('linearGradient')
                .attr('id', 'regionGradient')
                .attr('gradientUnits', 'userSpaceOnUse')
                .attr('x1', 0).attr('y1', height)
                .attr('x2', 0).attr('y2', 0)

            gradient.append('stop')
                .attr('offset', '0%')
                .attr('stop-color', colors.secondary)

            gradient.append('stop')
                .attr('offset', '100%')
                .attr('stop-color', isDarkMode.value ? '#A78BFA' : '#5eead4')

            g.selectAll('.bar')
                .data(data)
                .enter().append('rect')
                .attr('class', 'bar')
                .attr('x', d => x(d.region))
                .attr('width', x.bandwidth())
                .attr('y', height)
                .attr('height', 0)
                .attr('fill', 'url(#regionGradient)')
                .attr('rx', 4)
                .style('filter', 'drop-shadow(0 2px 4px rgba(0,0,0,0.1))')
                .transition()
                .duration(1000)
                .delay((d, i) => i * 100)
                .attr('y', d => y(d.count))
                .attr('height', d => height - y(d.count))

            g.append('g')
                .attr('transform', `translate(0, ${height})`)
                .call(d3.axisBottom(x))
                .selectAll('text')
                .attr('fill', colors.text)
                .style('text-anchor', 'end')
                .attr('dx', '-.8em')
                .attr('dy', '.15em')
                .attr('transform', 'rotate(-45)')

            g.append('g')
                .call(d3.axisLeft(y))
                .selectAll('text')
                .attr('fill', colors.text)
        }

        const createSentimentScatter = () => {
            if (!rawData.value.length) return

            const colors = getThemeColors()
            const data = rawData.value.filter(d =>
                d.sentimentSnownlp !== undefined &&
                d.rating !== undefined &&
                !isNaN(d.sentimentSnownlp) &&
                !isNaN(d.rating)
            ).slice(0, 500) // 性能限制

            const container = d3.select('#sentiment-scatter')
            const containerRect = container.node().getBoundingClientRect()
            const margin = { top: 20, right: 30, bottom: 50, left: 60 }
            const width = (containerRect.width || 400) - margin.left - margin.right
            const height = (containerRect.height || 300) - margin.top - margin.bottom

            container.selectAll('*').remove()

            const svg = container
                .append('svg')
                .attr('width', width + margin.left + margin.right)
                .attr('height', height + margin.top + margin.bottom)

            const g = svg.append('g')
                .attr('transform', `translate(${margin.left}, ${margin.top})`)

            const x = d3.scaleLinear()
                .domain([0, 1])
                .range([0, width])

            const y = d3.scaleLinear()
                .domain([1, 5])
                .range([height, 0])

            const color = d3.scaleOrdinal()
                .domain(['negative', 'neutral', 'positive'])
                .range([colors.negative, colors.neutral, colors.positive])

            const getCategory = (score) => {
                if (score >= 0.5) return 'positive'
                if (score >= 0.2) return 'neutral'
                return 'negative'
            }

            g.selectAll('.dot')
                .data(data)
                .enter().append('circle')
                .attr('class', 'dot')
                .attr('r', 0)
                .attr('cx', d => x(d.sentimentSnownlp))
                .attr('cy', d => y(d.rating))
                .attr('fill', d => color(getCategory(d.sentimentSnownlp)))
                .attr('opacity', 0.7)
                .style('filter', 'drop-shadow(0 1px 2px rgba(0,0,0,0.1))')
                .transition()
                .duration(1000)
                .delay((d, i) => i * 2)
                .attr('r', 4)

            g.append('g')
                .attr('transform', `translate(0, ${height})`)
                .call(d3.axisBottom(x))
                .selectAll('text')
                .attr('fill', colors.text)

            g.append('g')
                .call(d3.axisLeft(y))
                .selectAll('text')
                .attr('fill', colors.text)

            // 添加坐标轴标签
            g.append('text')
                .attr('transform', 'rotate(-90)')
                .attr('y', 0 - margin.left)
                .attr('x', 0 - (height / 2))
                .attr('dy', '1em')
                .style('text-anchor', 'middle')
                .attr('fill', colors.text)
                .text('评分')

            g.append('text')
                .attr('transform', `translate(${width / 2}, ${height + margin.bottom - 10})`)
                .style('text-anchor', 'middle')
                .attr('fill', colors.text)
                .text('情感得分 (SnowNLP)')
        }

        // 从 localStorage 加载主题
        onMounted(async () => {
            const savedTheme = localStorage.getItem('theme')
            if (savedTheme) {
                isDarkMode.value = savedTheme === 'dark'
            }

            await fetchData()

            // 延迟绘制所有图表，提升加载体验
            setTimeout(() => {
                if (stats.value) {
                    createSentimentPieChart()
                    createRatingBarChart()
                    createTimelineChart()
                    createRegionChart()
                    createSentimentScatter()
                }
            }, 100)

            // 监听窗口大小变化，重绘图表
            window.addEventListener('resize', () => {
                setTimeout(() => {
                    if (stats.value) {
                        createSentimentPieChart()
                        createRatingBarChart()
                        createTimelineChart()
                        createRegionChart()
                        createSentimentScatter()
                    }
                }, 100)
            })
        })

        return {
            stats,
            rawData,
            timeline,
            isDarkMode,
            error,
            toggleTheme,
            fetchData
        }
    }
}
</script>

<style>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

#app {
    font-family: 'Inter', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    min-height: 100vh;
    position: relative;
    overflow-x: hidden;
    transition: all 0.3s ease;
}

/* 主题背景 */
.dark-theme .background {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(135deg, #1f2937 0%, #111827 50%, #0f172a 100%);
    z-index: -2;
}

.light-theme .background {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(135deg, #f0fdfa 0%, #ccfbf1 30%, #a7f3d0 70%, #6ee7b7 100%);
    z-index: -2;
}

/* 主题切换按钮 */
.theme-toggle {
    position: fixed;
    top: 2rem;
    right: 2rem;
    width: 60px;
    height: 60px;
    border: none;
    border-radius: 50%;
    font-size: 1.5rem;
    cursor: pointer;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.3s ease;
}

.theme-toggle:hover {
    transform: scale(1.1);
}

/* 毛玻璃效果 */
.dark-theme .glass-morphism {
    background: rgba(255, 255, 255, 0.15);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid rgba(255, 255, 255, 0.2);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
    transition: all 0.3s ease;
}

.light-theme .glass-morphism {
    background: rgba(255, 255, 255, 0.25);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid rgba(255, 255, 255, 0.3);
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
    transition: all 0.3s ease;
}

.dark-theme .glass-morphism:hover {
    background: rgba(255, 255, 255, 0.2);
    transform: translateY(-2px);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}

.light-theme .glass-morphism:hover {
    background: rgba(255, 255, 255, 0.35);
    transform: translateY(-2px);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}

/* 头部区域 */
.header {
    text-align: center;
    padding: 3rem 2rem;
    position: relative;
}

.header-content {
    max-width: 600px;
    margin: 0 auto;
    padding: 2rem;
    border-radius: 20px;
}

.dark-theme .header h1 {
    margin: 0;
    font-size: 3rem;
    font-weight: 700;
    color: white;
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
    margin-bottom: 1rem;
}

.light-theme .header h1 {
    margin: 0;
    font-size: 3rem;
    font-weight: 700;
    color: #1f2937;
    text-shadow: 2px 2px 4px rgba(255, 255, 255, 0.3);
    margin-bottom: 1rem;
}

.dark-theme .header p {
    margin: 0;
    font-size: 1.2rem;
    color: rgba(255, 255, 255, 0.9);
    font-weight: 300;
}

.light-theme .header p {
    margin: 0;
    font-size: 1.2rem;
    color: #374151;
    font-weight: 300;
}

/* 主体容器 */
.container {
    max-width: 1400px;
    margin: 0 auto;
    padding: 0 2rem 4rem 2rem;
}

/* 统计卡片网格 */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 2rem;
    margin-bottom: 3rem;
}

.stat-card {
    padding: 2rem;
    border-radius: 20px;
    text-align: center;
    position: relative;
    overflow: hidden;
}

.stat-icon {
    font-size: 2.5rem;
    margin-bottom: 1rem;
    display: block;
}

.dark-theme .stat-card h3 {
    margin: 0 0 1rem 0;
    color: rgba(255, 255, 255, 0.8);
    font-size: 1rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    font-weight: 500;
}

.light-theme .stat-card h3 {
    margin: 0 0 1rem 0;
    color: #374151;
    font-size: 1rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    font-weight: 500;
}

.dark-theme .stat-value {
    font-size: 2.5rem;
    font-weight: 800;
    color: white;
    margin-bottom: 0.5rem;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}

.light-theme .stat-value {
    font-size: 2.5rem;
    font-weight: 800;
    color: #1f2937;
    margin-bottom: 0.5rem;
    text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.3);
}

.dark-theme .stat-percentage {
    font-size: 1rem;
    color: rgba(255, 255, 255, 0.7);
    font-weight: 500;
}

.light-theme .stat-percentage {
    font-size: 1rem;
    color: #6b7280;
    font-weight: 500;
}

/* 图表网格 */
.charts-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(450px, 1fr));
    gap: 2rem;
}

.chart-container {
    padding: 2rem;
    border-radius: 20px;
    position: relative;
    min-height: 400px;
}

.chart-container.full-width {
    grid-column: 1 / -1;
    min-height: 350px;
}

.chart-header {
    margin-bottom: 1.5rem;
    text-align: center;
}

.dark-theme .chart-header h2 {
    margin: 0 0 0.5rem 0;
    color: white;
    font-size: 1.5rem;
    font-weight: 600;
    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}

.light-theme .chart-header h2 {
    margin: 0 0 0.5rem 0;
    color: #1f2937;
    font-size: 1.5rem;
    font-weight: 600;
    text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.3);
}

.dark-theme .chart-subtitle {
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.9rem;
    font-weight: 400;
}

.light-theme .chart-subtitle {
    color: #6b7280;
    font-size: 0.9rem;
    font-weight: 400;
}

.chart-wrapper {
    height: calc(100% - 80px);
    min-height: 280px;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* 加载遮罩 */
.loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(10px);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    color: white;
}

.loading-spinner {
    width: 50px;
    height: 50px;
    border: 4px solid rgba(255, 255, 255, 0.3);
    border-top: 4px solid white;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    margin-bottom: 1rem;
}

/* 错误遮罩 */
.error-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.7);
    backdrop-filter: blur(10px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    color: white;
}

.error-message {
    background: rgba(239, 68, 68, 0.9);
    padding: 2rem;
    border-radius: 15px;
    text-align: center;
    max-width: 400px;
    margin: 0 2rem;
}

.error-message h3 {
    margin: 0 0 1rem 0;
    font-size: 1.5rem;
}

.error-message p {
    margin: 0 0 1.5rem 0;
    opacity: 0.9;
}

.retry-button {
    background: rgba(255, 255, 255, 0.2);
    border: 1px solid rgba(255, 255, 255, 0.3);
    color: white;
    padding: 0.75rem 1.5rem;
    border-radius: 8px;
    cursor: pointer;
    font-size: 1rem;
    transition: all 0.3s ease;
}

.retry-button:hover {
    background: rgba(255, 255, 255, 0.3);
    transform: translateY(-1px);
}

@keyframes spin {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}

/* 响应式设计 */
@media (max-width: 1200px) {
    .charts-grid {
        grid-template-columns: 1fr;
    }

    .chart-container {
        min-width: auto;
    }
}

@media (max-width: 768px) {
    .header h1 {
        font-size: 2rem;
    }

    .header p {
        font-size: 1rem;
    }

    .stats-grid {
        grid-template-columns: 1fr;
        gap: 1rem;
    }

    .charts-grid {
        gap: 1rem;
    }

    .chart-container {
        padding: 1.5rem;
        min-height: 350px;
    }

    .container {
        padding: 0 1rem 2rem 1rem;
    }

    .theme-toggle {
        top: 1rem;
        right: 1rem;
        width: 50px;
        height: 50px;
        font-size: 1.2rem;
    }
}

@media (max-width: 480px) {
    .header {
        padding: 2rem 1rem;
    }

    .stat-value {
        font-size: 2rem;
    }

    .chart-container {
        padding: 1rem;
        min-height: 300px;
    }
}

/* 图表专用样式 */
.bar:hover {
    opacity: 0.8;
    cursor: pointer;
}

.dot:hover {
    r: 6;
    cursor: pointer;
}

/* 所有交互元素平滑过渡 */
* {
    transition: all 0.3s ease;
}

/* 自定义滚动条 */
.dark-theme ::-webkit-scrollbar {
    width: 8px;
}

.dark-theme ::-webkit-scrollbar-track {
    background: rgba(255, 255, 255, 0.1);
}

.dark-theme ::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.3);
    border-radius: 4px;
}

.dark-theme ::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.5);
}

.light-theme ::-webkit-scrollbar {
    width: 8px;
}

.light-theme ::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.1);
}

.light-theme ::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.3);
    border-radius: 4px;
}

.light-theme ::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.5);
}
</style>